#include "ModbusClient.h"

//构造函数
ModbusClient::ModbusClient(QWidget *parent) : QWidget(parent)
{
    //1. 创建QModbusDevice对象
    modbusDevice = new QModbusTcpClient(this);

    // 连接错误显示
    connect(modbusDevice, &QModbusClient::errorOccurred, [this](QModbusDevice::Error) {
        qDebug() << "new Error: " << modbusDevice->errorString();
    });

    if (nullptr == modbusDevice)
    {
        qDebug() << "Cannot Create Modbus Client";
    }
    else
    {
        // 连接状态变化判断
        connect(modbusDevice, &QModbusClient::stateChanged, this, &ModbusClient::onModbusStateChanged);
    }

    _Timer = new QTimer(this);
    connect(_Timer,&QTimer::timeout,this,&ModbusClient::ModbusClientTimer);
    _Timer->start(5000);

}

//析构函数
ModbusClient::~ModbusClient()
{
    if (modbusDevice)
    {
        modbusDevice->disconnectDevice();
    }

    delete modbusDevice;
}

// 获取线圈读到的数据
bool ModbusClient::getReadData()
{
    return _readData;
}

QModbusDataUnit ModbusClient::getReadDataMap()
{
    return _readDataMap;
}

// 获取线圈写入的状态
bool ModbusClient::getWriteData()
{
    return _writeData;
}

bool ModbusClient::getConnectStatus()
{
    return _connectStatus;
}

// 读取线圈反馈
void ModbusClient::onReadReady()
{
    _readData = false;
    QModbusDataUnit tmp;
    _readDataMap = tmp;

    auto reply = qobject_cast<QModbusReply*>(sender());
    if (nullptr == reply)
    {
        return;
    }

    //判断是否出错
    if (reply->error() == QModbusDevice::NoError)
    {
        //读取响应数据
        const QModbusDataUnit responseData = reply->result();

//        qDebug() << "读到数据为：" << responseData.values();
//        ui->textEdit->append("读取数据为：" + QString::number(responseData.value(0)));
//        _readData = responseData.value(0);
        _readDataMap = responseData;
    }
    else if (reply->error() == QModbusDevice::ProtocolError)
    {
        qDebug() << "Read response Protocol error: " << reply->errorString();
    }
    else
    {
        qDebug() << "Read response Error: " << reply->errorString();
    }

    //删除reply
    reply->deleteLater();
}

// Modbus设备连接状态判断
void ModbusClient::onModbusStateChanged(int state)
{
    //判断Modbus设备连接是否处于连接状态
    if (state == QModbusDevice::UnconnectedState)
    {
        qDebug() << "TCP Client连接到Server 未连接 IP:"<<_Ip<<" Port:"<<_Port;
        _connectStatus=false;//PLC设备连接失败
    }
    else if (state == QModbusDevice::ConnectingState)
    {
        qDebug() << "TCP Client正在连接Server";
        _connectStatus=false;//PLC设备连接失败
    }
    else if (state == QModbusDevice::ConnectedState)
    {
        qDebug() << "TCP Client已经连接到Server";
        _connectStatus=true;//PLC设备连接成功
    }
    else if (state == QModbusDevice::ClosingState)
    {
        qDebug() << "设备已经被关闭";
        _connectStatus=false;//PLC设备连接失败
    }
}

// 设备连接函数
void ModbusClient::on_btnConnect_clicked(QString ip, int port)
{
    if (!modbusDevice)
    {
        return;
    }

    if (modbusDevice->state() != QModbusDevice::ConnectedState)
    {
        _Ip=ip;
        _Port=port;
        modbusDevice->setConnectionParameter(QModbusDevice::NetworkAddressParameter, ip);
        modbusDevice->setConnectionParameter(QModbusDevice::NetworkPortParameter, port);

        //设置超时时间
        modbusDevice->setTimeout(1000); //1秒
        //设置失败重试次数
        modbusDevice->setNumberOfRetries(3);

        //连接到服务端
        bool ok = modbusDevice->connectDevice();
        if (!ok)
        {
            qDebug() << "modbusDevice->connectDevice failed-->ip:"<<ip;
        }

    }
    else
    {
        //断开连接
        modbusDevice->disconnectDevice();
    }

}

// 读线圈
void ModbusClient::on_ReadCoil(int address, int number)
{
    // 加锁，确保互斥访问
    QMutexLocker locker(&mutex);

    if (modbusDevice->state() != QModbusDevice::ConnectedState)
    {
        return;
    }
    //QModbusDataUnit::Coils 从地址 address 开始读取 1 个线圈值
    QModbusDataUnit data(QModbusDataUnit::Coils, address, number);

    auto reply = modbusDevice->sendReadRequest(data, 1);

    if (nullptr == reply)
    {
        qDebug() << "发送请求数据失败: " << modbusDevice->errorString();
    }
    else
    {
        if (!reply->isFinished())
        {
            connect(reply, &QModbusReply::finished, this, &ModbusClient::onReadReady);
        }
        else
        {
            //broadcast replies return immediately
            delete reply;
        }
    }
}

// 写线圈
void ModbusClient::on_WriteCoil(int address, quint16 value)
{
    // 加锁，确保互斥访问
    QMutexLocker locker(&mutex);

    if (modbusDevice->state() != QModbusDevice::ConnectedState)
    {
        return;
    }
    //从地址0开始写 1 个线圈的值
    QModbusDataUnit writeData(QModbusDataUnit::Coils, address, 1);

    // 第 0 位，置 value
    writeData.setValue(0, value);

//    qDebug() << "发送的数据为： " << writeData.values();
//    ui->textEdit->append("写入数据为：" + QString::number(writeData.value(0)));
    _writeData = writeData.value(0);

    QModbusReply* reply = modbusDevice->sendWriteRequest(writeData, 1);
    if (reply)
    {
        if (!reply->isFinished())
        {
            //接收响应信息
            connect(reply, &QModbusReply::finished, this, [reply](){
                if (reply->error() == QModbusDevice::ProtocolError)
                {
                    //接收到的响应信息是协议错误
                    qDebug() << "写入数据错误：" << reply->errorString();
                }
                else if (reply->error() != QModbusDevice::NoError)
                {
                    //接收到的响应消息是其它错误
                    qDebug() << "写入数据错误： " << reply->errorString();
                }
                else
                {
                    //接收到的消息没有错误 一般没有必要解析响应消息
                    const QModbusDataUnit data = reply->result();
                    qDebug() << "消息数据个数：" << data.valueCount() << " :" << data.values();
                }
                reply->deleteLater();
            });
        }
        else
        {
            //发送没有响应数据
            //broadcast replies return immediately
            reply->deleteLater();
        }
    }
    else
    {
        qDebug() << "sendWriteRequest Error: " << reply->errorString();
    }
}

void ModbusClient::ModbusClientTimer()
{
    if(!_connectStatus && _Ip!="" && _Port!=-1){
        // 设备连接函数
        on_btnConnect_clicked(_Ip,_Port);
    }
}

